using System;
using System.Collections.Generic;
using System.Text;
using surfInterop;
using System.Runtime.Remoting;
using System.Drawing;

namespace Blimpbots.Recognition
{

    /// <summary>
    /// A self-recycling singleton class providing access to the
    /// SURF libraries through an additional AppDomain.
    /// 
    /// This is designed to clear up memory leak problems, but it's
    /// not 100% clear that it's working yet.
    /// 
    /// </summary>
    public class SurfRemoted : IDisposable
    {
        private static readonly int MaxCycles = 100;
        private static SurfRemoted _instance = new SurfRemoted();
        private static int engineCycles = 0;

        private surfInterop.SurfEngine surfEngine = null;
        private AppDomain surfDomain = null;

        public static SurfRemoted Instance
        {
            get
            {
                // Periodically unload the AppDomain
                if (_instance == null || engineCycles >= MaxCycles)
                {
                    _instance.Dispose();
                    _instance = new SurfRemoted();
                    engineCycles = 0;
                }

                engineCycles++;

                return _instance;
            }
        }

        public SurfRemoted()
        {
            surfDomain = AppDomain.CreateDomain("SurfRemoteDomain");
            ObjectHandle oh = surfDomain.CreateInstance("surfInterop", "surfInterop.SurfEngine");
            surfEngine = (surfInterop.SurfEngine)oh.Unwrap();
        }

        /// <summary>
        /// Computes the SURF keypoints for a region of an image.
        /// </summary>
        public SurfKeypoint[] computeKeypoints(Bitmap img, Rectangle region)
        {
            region = FixRectangle(region, img);
            System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();
            w.Start();
            GrayscaleImage grayImg = new GrayscaleImage(img, region);
            w.Stop();
            Console.WriteLine("Convert:" + w.ElapsedMilliseconds.ToString());
            SurfKeypoint[] kpts = surfEngine.computeKeypoints(grayImg.Scan0, grayImg.Width, grayImg.Height, 4.0);

            for (int i = 0; i < kpts.Length; i++)
            {
                kpts[i].x += (double)region.Left;
                kpts[i].y += (double)region.Top;
            }

            return kpts;
        }

        private Rectangle FixRectangle(Rectangle region, Bitmap bitmap)
        {
            int left = region.Left;
            int right = region.Right;
            int top = region.Top;
            int bottom = region.Bottom;

            if (left < 0) left = 0; else if (left >= bitmap.Width) left = bitmap.Width - 1;
            if (right < 0) right = 0; else if (right >= bitmap.Width) right = bitmap.Width - 1;
            if (top < 0) top = 0; else if (top >= bitmap.Height) top = bitmap.Height - 1;
            if (bottom < 0) bottom = 0; else if (bottom >= bitmap.Height) bottom = bitmap.Height - 1;

            region = new Rectangle(left, top, right - left, bottom - top);

            return region;
        }

        public SurfKeypoint[] computeKeypoints(GrayscaleImage grayImg)
        {
            return surfEngine.computeKeypoints(grayImg.Scan0, grayImg.Width, grayImg.Height, 4.0);
        }

        public SurfKeypoint[] computeKeypoints(GrayscaleImage grayImg, double blobThresh)
        {
            surfInterop.SurfKeypoint[] keypoints =
                surfEngine.computeKeypoints(grayImg.Scan0, grayImg.Width, grayImg.Height, blobThresh);

            return keypoints;
        }


        #region IDisposable Members

        public void Dispose()
        {
            surfEngine = null;
            AppDomain.Unload(surfDomain);
        }

        #endregion
    }
}
